use crate::controller::Error;
use crate::service::ServiceSet;
use crate::{dto, mq};
use std::sync::Arc;

pub struct NetworkAclRuleController {
    service_set: Arc<ServiceSet>,
    event_handler: EventHandler,
}

impl NetworkAclRuleController {
    pub fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set: service_set.clone(),
            event_handler: EventHandler::new(service_set, mq_client_set),
        }
    }
    pub async fn set_ingress_rules(
        &self,
        request: &dto::network_acl_rule::SetRulesRequest,
    ) -> Result<(), Error> {
        self.service_set
            .network_acl_rule
            .set_ingress_rules(
                request.network_acl_id.as_str(),
                request.params.rules.as_slice(),
            )
            .await?;

        Ok(())
    }

    pub async fn set_egress_rules(
        &self,
        request: &dto::network_acl_rule::SetRulesRequest,
    ) -> Result<(), Error> {
        self.service_set
            .network_acl_rule
            .set_egress_rules(
                request.network_acl_id.as_str(),
                request.params.rules.as_slice(),
            )
            .await?;

        self.event_handler
            .spawn_handle_egress_rules_setup_event(request.network_acl_id.as_str());

        Ok(())
    }

    pub async fn all_ingress_rules(
        &self,
        request: &dto::network_acl_rule::AllIngressRulesRequest,
    ) -> Result<dto::network_acl_rule::AllIngressRulesResponseData, Error> {
        let list = self
            .service_set
            .network_acl_rule
            .all_ingress_rules(request.network_acl_id.as_str())
            .await?;

        Ok(dto::network_acl_rule::AllIngressRulesResponseData { list })
    }

    pub async fn all_egress_rules(
        &self,
        request: &dto::network_acl_rule::AllEgressRulesRequest,
    ) -> Result<dto::network_acl_rule::AllEgressRulesResponseData, Error> {
        let list = self
            .service_set
            .network_acl_rule
            .all_egress_rules(request.network_acl_id.as_str())
            .await?;

        Ok(dto::network_acl_rule::AllEgressRulesResponseData { list })
    }
}

struct EventHandler {
    service_set: Arc<ServiceSet>,
    mq_client_set: Arc<mq::client::ClientSet>,
}

impl EventHandler {
    fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set,
            mq_client_set,
        }
    }
    fn spawn_handle_egress_rules_setup_event(&self, network_acl_id: &str) {
        let network_acl_id = network_acl_id.to_string();
        let handler = Self::new(self.service_set.clone(), self.mq_client_set.clone());
        tokio::spawn(async move {
            if let Err(e) = handler
                .handle_egress_rules_setup_event(network_acl_id.as_str())
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }

    async fn handle_egress_rules_setup_event(&self, network_acl_id: &str) -> Result<(), Error> {
        let limit = wanmesh_common::dto::LIMIT_MAX;
        let mut offset = 0;
        loop {
            let traffic_steering_rule_list = self
                .service_set
                .traffic_steering_rule
                .list(
                    &dto::core::traffic_steering_rule::Filter {
                        network_acl_id: Some(network_acl_id.to_string()),
                        ..Default::default()
                    },
                    offset,
                    limit,
                )
                .await?;

            if traffic_steering_rule_list.is_empty() {
                break;
            }

            self.handle_egress_rules_setup_event_by_traffic_steering_rule_list(
                &traffic_steering_rule_list,
            )
            .await?;

            if traffic_steering_rule_list.len() < limit as usize {
                break;
            }

            offset = offset + limit;
        }

        Ok(())
    }

    async fn handle_egress_rules_setup_event_by_traffic_steering_rule_list(
        &self,
        traffic_steering_rule_list: &[dto::core::traffic_steering_rule::TrafficSteeringRuleItem],
    ) -> Result<(), Error> {
        for traffic_steering_rule in traffic_steering_rule_list {
            self.handle_egress_rules_setup_event_by_traffic_steering_rule(traffic_steering_rule)
                .await?;
        }

        Ok(())
    }

    async fn handle_egress_rules_setup_event_by_traffic_steering_rule(
        &self,
        traffic_steering_rule: &dto::core::traffic_steering_rule::TrafficSteeringRuleItem,
    ) -> Result<(), Error> {
        let endpoint = self
            .service_set
            .endpoint_manager
            .get(traffic_steering_rule.inner.endpoint_id.as_str())
            .await?;

        self
            .mq_client_set
            .node
            .endpoint
            .traffic_steering_rule
            .network_acl
            .publish_egress_rules_setup_event(
                endpoint.inner.node_id.as_str(),
                dto::event::endpoint::traffic_steering_rule::network_acl::EgressRulesSetupEventPayload {
                    endpoint_id: traffic_steering_rule.inner.endpoint_id.clone(),
                    traffic_steering_rule_id: traffic_steering_rule
                        .traffic_steering_rule_id
                        .clone(),
                    network_acl_id: traffic_steering_rule.inner.network_acl_id.clone(),
                },
            )
            .await?;

        Ok(())
    }
}
